using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Security.Cryptography;
using System.Xml.Serialization;
using System.Collections.Specialized;


// Description needed by Son of SmartPart
[System.ComponentModel.Description("LTI Tool Consumer Sakai Spec WebPart")]
public partial class LaunchPartSakai : System.Web.UI.UserControl
{
    // Constants
    const string cWebMethod = "POST";
    const string cWebContentType = "application/x-www-form-urlencoded";
    struct LtiTypes { public static string Simple = "Simple"; public static string Basic = "Basic";}

    // Local variables
    private const string defaultText = "";
    // These are defaults for testing purposes only
    private string _providerURL = "None";
    private string _sharedSecret = "secret";
    private string _tpResourceId = "None";
    private string _courseID = "Test Course";
    private string _courseName = "Test Course";
    private string _toolID;
    private string _signature;
    private string _action = "launchresolve";
    private string _key = "12345";
    private string _ltiType;
    private string _baseSignature;
    private string _xmlUrl;
    public string PostParams;
    string nonce;

    private bool _debug = false;
    private string _debugPost;
    private string _debugResponse;

    // Name Value Collection to hold the parameters sent in post
    private NameValueCollection PostFields = new NameValueCollection();

    private bool debug = false;

    // Properties
    [Browsable(true), Category("XML Url"), Description("XML File to Load Basic LTI")]
    public string XMLUrl
    {
        get { return _xmlUrl; }
        set { _xmlUrl = value; }
    }

    [Browsable(true), Category("LTI Type"), Description("LTI Type")]
    public string LtiType
    {
        get { return _ltiType; }
        set { _ltiType = value; }
    }

    [Browsable(true), Category("Tool Provider URL"), Description("Tool Provider URL")]
    public string ProviderURL
    {
        get { return _providerURL; }
        set { _providerURL = value; }
    }

    [Browsable(true), Category("Shared Secret"), Description("Tool Provider Shared Secret")]
    public string SharedSecret
    {
        get { return _sharedSecret; }
        set { _sharedSecret = value; }
    }

    [Browsable(true), Category("TP Resource ID"), Description("TP Resource ID")]
    public string TPResourceID
    {
        get { return _tpResourceId; }
        set { _tpResourceId = value; }
    }

    [Browsable(true), Category("Course ID"), Description("Course ID")]
    public string CourseID
    {
        get { return _courseID; }
        set { _courseID = value; }
    }

    [Browsable(true), Category("Course Name"), Description("Course Name")]
    public string CourseName
    {
        get { return _courseName; }
        set { _courseName = value; }
    }

    [Browsable(true), Category("Tool ID"), Description("Tool ID")]
    public string ToolID
    {
        get { return _toolID; }
        set { _toolID = value; }
    }


    [Browsable(true), Category("Launch Signature"), Description("Launch Signature")]
    public string Signature
    {
        get { return _signature; }
        set { _signature = value; }
    }

    [Browsable(true), Category("Key"), Description("Key")]
    public string Key
    {
        get { return _key; }
        set { _key = value; }
    }

    // Page Load
    protected void Page_Load(object sender, EventArgs e)
    {

        ifrContent.Visible = false; // Hide iFrame
        lblStatus.Text = string.Empty; // Clear Status Text
        pnlwidget.Visible = false; // Hide Widget panel
        btnLaunch.Visible = false;
        btnLaunch_Click(sender, e);
    }

    // On launch click 
    protected void btnLaunch_Click(object sender, EventArgs e)
    {
        string responseFromToolProvider = string.Empty;
        // Fill in values
        

        //PostParams = FillPostParams();
        // Post data
        string ErrorMessage = FillData();

        if (ErrorMessage == "")
            responseFromToolProvider = SendPostData();
        else
        {
            lblResponse.Text = ErrorMessage;
            return;
        }

        // Parse Response
        ParseData(responseFromToolProvider);
    }

    private string FillPostParams()
    {
        StringBuilder sb = new StringBuilder();
        OAuth.OAuthBase obase = new OAuth.OAuthBase();
        foreach (string name in PostFields.AllKeys)
        {
            sb.Append(obase.UrlEncode(name));
            sb.Append("=");
            sb.Append(obase.UrlEncode(PostFields[name]));
            sb.Append("&");
        }
        sb.Remove(sb.Length - 1, 1);
        return sb.ToString();
    }

    // Utility function that fills in the name value pairs
    private string FillData()
    {
        nonce = GetNonce();
        string dateStamp = GetDateStamp();
        OAuth.OAuthBase ob = new OAuth.OAuthBase();
        List<OAuth.OAuthBase.QueryParameter> QPs = new List<OAuth.OAuthBase.QueryParameter>();
        string timestamp = ob.GenerateTimeStamp();

        System.IO.StreamReader sr = new StreamReader(_xmlUrl);
        XmlSerializer xs = new XmlSerializer(typeof(Blti));
        Blti bInfo = null;
        try { bInfo = (Blti)xs.Deserialize(sr); }
        catch (Exception E) { return E.Message; }

        if (bInfo.LaunchUrl != "")
            _providerURL = bInfo.LaunchUrl;
        else
            _providerURL = bInfo.SecureLaunchUrl;

        AddParameter("lti_message_type", "basic-lti-launch-request");
        AddParameter("lti_version", "LTI-1p0");
        AddParameter("resource_link_id", "120988f929-274612");
        AddParameter("resource_link_title", bInfo.Title);
        AddParameter("resource_link_description", bInfo.Description);
        AddParameter("user_id", "292832126");
        AddParameter("roles", "Instructor");
        AddParameter("lis_person_name_given", "Jane");
        AddParameter("lis_person_name_family", "Public");
        AddParameter("lis_person_name_full", "Jane Q. Public");
        AddParameter("lis_person_contact_email_primary", "user@school.edu");
        AddParameter("context_id", "456434513");
        AddParameter("context_type", "CourseSection");
        AddParameter("context_title", "Design of Personal Environments");
        AddParameter("context_label", "SI182");
        AddParameter("launch_presentation_locale", "en_US_variant");
        AddParameter("launch_presentation_document_target", "iframe");
        AddParameter("launch_presentation_width", "320");
        AddParameter("launch_presentation_height", "240");
        AddParameter("launch_presentation_return_url", "http://www.mirlearn.com");
        AddParameter("tool_consumer_instance_guid", "lmsng.school.edu");
        AddParameter("tool_consumer_instance_name", "SchoolU");
        AddParameter("tool_consumer_instance_description", "University of School (LMSng)");
        AddParameter("tool_consumer_instance_url", "http://lmsng.school.edu");
        AddParameter("tool_consumer_instance_contact_email", bInfo.Vendor.Contact.Email);

        AddParameter("oauth_version", "1.0");
        AddParameter("oauth_nonce", "1d1c79ce226d67a46db26b0c56b02b93");
        AddParameter("oauth_timestamp", timestamp);
        if (bInfo.Custom.Length > 0 && bInfo.Custom[0].Name.ToLower() == "keyname")
            AddParameter("oauth_consumer_key", bInfo.Custom[0].Value);
        else
            AddParameter("oauth_consumer_key", "12345");
        AddParameter("oauth_callback", "about:blank");
        AddParameter("oauth_signature_method", "HMAC-SHA1");
        //AddParameter("lis_person_sourcedid", "school.edu:user"); 
        //AddParameter("basiclti_submit", "Launch Endpoint with BasicLTI Data");

        foreach (string kv in PostFields)
            QPs.Add(new OAuth.OAuthBase.QueryParameter(kv, PostFields[kv]));
        _baseSignature = ob.GenerateSignatureBase(new Uri(_providerURL.ToLower()), QPs);
        AddParameter("oauth_signature", ob.GenerateSignature(_baseSignature, "secret", ""));

        return "";
    }

    // Utility function that adds name/value pair only if it has data
    private void AddParameter(string name, string value)
    {
        if (!string.IsNullOrEmpty(name)
            && !string.IsNullOrEmpty(value))
        {
            PostFields.Add(name, value);
        }
    }

    // Security Function to create nonce (Just a GUID in our case)
    private string GetNonce()
    {
        System.Guid nonce = System.Guid.NewGuid();
        return nonce.ToString();
    }

    // Utility Function for security
    private string GetDateStamp()
    {
        DateTime dateStamp = DateTime.Now;
        return dateStamp.ToString("yyyy-MM-ddTHH:mm:ssZ");
    }

    // Security Function to get SHA1 Digest from nonce, datestamp and secret
    private string GetDigest(string nonce, string dateStamp, string secret)
    {
        String presha1 = nonce + dateStamp + secret;
        return GetSHA(presha1);

    }
    private string GetSHA(string presha1)
    {
        
        String sha1 = null;
        try
        {
            UTF8Encoding encoding = new UTF8Encoding();
            byte[] hashBytes = encoding.GetBytes(presha1);

            // compute SHA-1 hash..
            SHA1 md = new SHA1CryptoServiceProvider();
            byte[] cryptstr = md.ComputeHash(hashBytes);

            try
            {
                sha1 = Convert.ToBase64String(cryptstr);   
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Encode" + e.Message);
            }
        }
        catch (NotSupportedException)
        {
        }

        return sha1;
        
    }

    private string MakeSignature()
    {
        string purl = _providerURL.Replace(":", "%3A").Replace("/", "%2F").Replace("&", "%26").Replace("=", "%3D") + "&";
        string param = "";
        foreach (string key in PostFields.AllKeys)
            param += "&" + key + "=" + PostFields[key];
        param = param.Remove(0, 1);
        param = param.Replace(":", "%3A").Replace("/", "%2F").Replace("&", "%26").Replace("=", "%3D");
        return "POST&" + purl + param;
    }

    // User and Course Functions
    private string GetUserID()
    {
        string userID = string.Empty;
        userID = "12";
        return userID;
    }

    private string GetUserEmail()
    {
        string email = string.Empty;
        email = "user@test.com";
        return email;
    }
    private string GetUserFirstName()
    {
        string userName = string.Empty;
        userName = "Test";
        return userName;
    }

    private string GetUserLastName()
    {
        string userName = string.Empty;
        userName = "User";
        return userName;
    }

    private string GetUserRole()
    {
        string userRole = "Student";
        return userRole;
    }

    // This is set as a property of the Web Part
    private string GetCourseID()
    {
        string courseID;
        courseID = System.Guid.NewGuid().ToString();
        return courseID;
    }

    // This is set as a property of the Web Part
    private string GetCourseName()
    {

        return _courseName;
    }

    private string GetOrgID()
    {
        return "IMS";
    }


    // Function to actually compose and send message to the tool
    private string SendPostData()
    {
        String response = string.Empty;

        // Create URI and request
        Uri URI = new Uri(_providerURL);
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(URI);

        // Request Type
        webRequest.Method = cWebMethod;

        // Headers
        webRequest.ContentType = cWebContentType;

        // Parameters
        ASCIIEncoding encoding = new ASCIIEncoding();
        StringBuilder sb = new StringBuilder();
        OAuth.OAuthBase obase = new OAuth.OAuthBase();
        foreach (string name in PostFields.AllKeys)
        {
            sb.Append(obase.UrlEncode(name));
            sb.Append("=");
            sb.Append(obase.UrlEncode(PostFields[name]));
            sb.Append("&");
        }
        sb.Remove(sb.Length - 1, 1);
        byte[] buffer = encoding.GetBytes(sb.ToString());

        //
        if (_debug) lblPost.Text = "<pre>" + sb.ToString() + "</pre>";

        
        // Get length of content
        webRequest.ContentLength = buffer.Length;

        // Get request stream
        Stream newStream = webRequest.GetRequestStream();

        // Send the data.
        newStream.Write(buffer, 0, buffer.Length);

        // Close stream
        newStream.Close();

        // Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable.
        HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

        // Display the contents of the page to the console.
        Stream streamResponse = webResponse.GetResponseStream();

        // Get stream object
        StreamReader streamRead = new StreamReader(streamResponse);

        Char[] readBuffer = new Char[256];

        // Read from buffer
        int count = streamRead.Read(readBuffer, 0, 256);
        
        while (count > 0)
        {
            // get string
            String resultData = new String(readBuffer, 0, count);

            response += resultData;

            // Read from buffer
            count = streamRead.Read(readBuffer, 0, 256);
        }

        // Release the response object resources.
        streamRead.Close();
        streamResponse.Close();

        // Close response
        webResponse.Close();
        
        //
        if (_debug) lblResponse.Text = "<pre>" + response + "</pre>";

        return response;
    }


    // Function to parse the response from the tool and determine if it is a widget, or iframe
    private void ParseData(string response)
    {
        if (!response.Contains("<launchUrl>"))
        {
            Response.Write(response);//Response.Write(response + "<br>" + _baseSignature);
            return;
        }

        if (_action == "direct")
            return;

        ifrContent.Attributes["src"] = response.Replace("<launchUrl>", "").Replace("</launchUrl>", "");
        ifrContent.Visible = true;

        return;

        string status = string.Empty;
        string launchType = string.Empty;
        string launchData = string.Empty;
        // Parse XML return data
        XmlDocument x = new XmlDocument();
        XmlNodeList xnl;
        x.Load(new StringReader(response));

        // Check for Status
        xnl = x.SelectNodes("//status");
        status = GetNodeList(xnl);

        // If failed then show error
        if (status.ToLower() != "success")
        {
            xnl = x.SelectNodes("//code");
            lblStatus.Text = GetNodeList(xnl);
            lblStatus.Text += " - ";
            xnl = x.SelectNodes("//description");
            lblStatus.Text += GetNodeList(xnl);
        }
        else // If success then determine widget or iFrame
        {
            xnl = x.SelectNodes("//type");
            launchType = GetNodeList(xnl);
            if (launchType.ToLower() == "iframe")
            {
                xnl = x.SelectNodes("//launchUrl");
                launchData = GetNodeList(xnl);
                ifrContent.InnerHtml = launchData;
                ifrContent.Attributes["src"] = launchData;
                ifrContent.Visible = true;
            }

            if (launchType.ToLower() == "widget")
            {
                xnl = x.SelectNodes("//widget");
                launchData = GetNodeList(xnl);
                pnlwidget.Controls.Add(new LiteralControl(launchData));
                pnlwidget.Visible = true;
            }
        }

        if (debug) Response.Write("<pre>" + Server.HtmlEncode(response) + "</pre>");
    }

    // Utility Function to get all values in a node/nodelist
    private string GetNodeList(XmlNodeList xnl)
    {
        string nodeValue = string.Empty;
        foreach (XmlNode xNode in xnl)
        {
            nodeValue += xNode.InnerText;
        }
        return nodeValue;
    }

    
}
